/*
 * ©2023 JOY-HUBS.COM
 */

package com.joyhubs.base.advice;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpRequest;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import com.joyhubs.base.config.JoyWebAutoConfiguration;
import com.joyhubs.base.config.JoyWebMvcConfig;
import com.joyhubs.base.config.JoyWebProperties;
import com.joyhubs.base.entity.ContentBody;

/**
 * 全局响应数据格式处理
 * <p>
 * NOTICE: have to config jackson2 http convert in the top of converter list. {@link JoyWebMvcConfig#contentBodyConverter()}
 *
 * @author zhiwu.zzw
 */
@Slf4j
@RestControllerAdvice
@RequiredArgsConstructor
@ConditionalOnBean(JoyWebAutoConfiguration.class)
public class ResponseWrapperAdvice implements ResponseBodyAdvice<Object> {
    
    private final JoyWebProperties webProperties;
    
    /**
     * swagger-ui api doc path
     */
    @Value("${springdoc.api-docs.path:/v3/api-docs}")
    private String apiDocsPath;
    
    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        // 反馈结果为ContentBody类型不需要二次封装
        return !returnType.getParameterType().equals(ContentBody.class);
    }
    
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 对接口地址也做检查，只处理固定前缀接口的返回值
        String servletPath = ((ServletServerHttpRequest) request).getServletRequest().getServletPath();
        // 如果响应结果不需要包裹
        if (this.isIgnoreWrapRequest(servletPath) || this.isApiDocRequest(servletPath)) {
            return body;
        }
        if (!(body instanceof ContentBody)) {
            // 通过ContentBody全局封装业务响应数据
            return ContentBody.success(body);
        }
        return body;
    }
    
    /**
     * judge requestPath whether api doc path
     *
     * @param requestPath
     * @return
     */
    private boolean isApiDocRequest(String requestPath) {
        if (StringUtils.hasText(requestPath)) {
            // springdoc.api-docs.path=/v3/api-docs
            return requestPath.startsWith(apiDocsPath);
        }
        return false;
    }
    
    /**
     * check ignore wrap response body
     *
     * @param requestPath
     * @return
     */
    private boolean isIgnoreWrapRequest(String requestPath) {
        if (CollectionUtils.isNotEmpty(webProperties.getIgnoreWrapPaths())) {
            return webProperties.getIgnoreWrapPaths().stream().filter(path -> requestPath.startsWith(path)).count() > 0;
        }
        return false;
    }
    
}
